﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using Netfraction.Common.DataStorage;
using Netfraction.Common.Logging;

namespace NetfractionHub.Settings
{
	public static class HubSettings
	{
		private static bool _documentLoaded;
		private static XmlDataStore _settingsStore;

		private static bool CheckStoreExists()
		{
			_settingsStore = new XmlDataStore(HubDataSource.Source, "Hub.xml");
			return _settingsStore != null;
		}

		public static void LoadSettings()
		{
			if (!CheckStoreExists())
				return;

			LogManager.WriteEntry("Loading Hub Settings from: " + _settingsStore.FullPath, LogEventId.LoadingHubSettings, LogLevel.Basic);

			FileInfo f = new FileInfo(_settingsStore.FullPath);

			if (_settingsStore.IsEmpty())
				CreateSettingsFile();

			XPathNavigator xNav = _settingsStore.CreateNavigator(false);

			HubIdentity.Name = xNav.SelectSingleNode(@"//Settings/HubIdentity/Name").Value;
			HubIdentity.Description = xNav.SelectSingleNode(@"//Settings/HubIdentity/Description").Value;
			HubIdentity.Website = xNav.SelectSingleNode(@"//Settings/HubIdentity/Website").Value;
			HubIdentity.HubOwner = xNav.SelectSingleNode(@"//Settings/HubIdentity/HubOwner").Value;
			HubIdentity.MessageOfTheDay = xNav.SelectSingleNode(@"//Settings/HubIdentity/MessageOfTheDay").Value;
			HubIdentity.UserInterfaceClientId = xNav.SelectSingleNode(@"//Settings/HubIdentity/UserInterfaceClientId").Value;
			HubIdentity.FailoverAddresses = xNav.SelectSingleNode(@"//Settings/HubIdentity/FailoverAddresses").Value;

			System.Net.IPEndPoint iep;
			System.Net.DnsEndPoint dep;
			foreach (XPathNavigator node in xNav.SelectSingleNode(@"//Settings/HubConnection/Listeners").SelectChildren(XPathNodeType.Element))
			{
				iep = null;
				dep = null;
				try
				{
					bool parseSuccess = NetfractionHub.Network.IPHelper.ParseIPEndPoint(node.Value, out iep);

					if (!parseSuccess)
						parseSuccess = NetfractionHub.Network.IPHelper.ParseDnsEndPoint(node.Value, out dep);

					if (parseSuccess)
						HubConnection.ListeningEndPoints.Add(iep == null ? (System.Net.EndPoint)dep : iep);
				}
				catch (FormatException)
				{
					Console.WriteLine("{0} is invalid", node.Value);
				}
				catch (Exception ex)
				{
					Console.WriteLine("{0} is invalid - {1}", node.Value, ex.Message);
				}
			}
			foreach (XPathNavigator node in xNav.Select(@"//Settings/HubConnection/Rules/DefaultRole"))
			{
				HubConnection.Rules.DefaultRole defRole = new HubConnection.Rules.DefaultRole();
				defRole.RoleId = Convert.ToByte(node.SelectSingleNode("@roleId").Value);
				XPathNavigator xAncNav = node.SelectSingleNode("@ancestorId");
				if (xAncNav != null)
					defRole.AncestorId = Convert.ToByte(xAncNav.Value);

				defRole.IPRangeStart = System.Net.IPAddress.Parse(node.SelectSingleNode("IPRangeStart").Value);
				defRole.IPRangeEnd = System.Net.IPAddress.Parse(node.SelectSingleNode("IPRangeEnd").Value);
				HubConnection.Rules.DefaultRoles.Add(defRole);
			}
			HubConnection.IdentifyTimeout = Convert.ToInt32(xNav.SelectSingleNode(@"//Settings/HubConnection/IdentifyTimeout").Value);
			HubConnection.MaximumUsersCount = Convert.ToInt32(xNav.SelectSingleNode(@"//Settings/HubConnection/MaximumUsersCount").Value);
			HubConnection.ADCS.IsEnabled = Convert.ToBoolean(xNav.SelectSingleNode(@"//Settings/HubConnection/ADCS/IsEnabled").Value);

			SecurityBot.ClientId = xNav.SelectSingleNode(@"//Settings/HubBots/SecurityBot/ClientId").Value;
			SecurityBot.SessionId = xNav.SelectSingleNode(@"//Settings/HubBots/SecurityBot/SessionId").Value;
			SecurityBot.Description = xNav.SelectSingleNode(@"//Settings/HubBots/SecurityBot/Description").Value;

			OperatorBot.ClientId = xNav.SelectSingleNode(@"//Settings/HubBots/OperatorBot/ClientId").Value;
			OperatorBot.SessionId = xNav.SelectSingleNode(@"//Settings/HubBots/OperatorBot/SessionId").Value;
			OperatorBot.Description = xNav.SelectSingleNode(@"//Settings/HubBots/OperatorBot/Description").Value;

			NicknameRules.MinimumLength = Convert.ToInt32(xNav.SelectSingleNode(@"//Settings/NicknameRules/MinimumLength").Value);
			NicknameRules.MaximumLength = Convert.ToInt32(xNav.SelectSingleNode(@"//Settings/NicknameRules/MaximumLength").Value);
			NicknameRules.AllowedCharacters = xNav.SelectSingleNode(@"//Settings/NicknameRules/AllowedCharacters").Value;

			Chat.MaximumMessageLength = Convert.ToInt32(xNav.SelectSingleNode(@"//Settings/Chat/MaximumMessageLength").Value);
			Chat.MaximumMessageLines = Convert.ToInt32(xNav.SelectSingleNode(@"//Settings/Chat/MaximumMessageLines").Value);

			SharingRules.MinimumShareSize = Convert.ToInt64(xNav.SelectSingleNode(@"//Settings/SharingRules/MinimumShareSize").Value);
			SharingRules.MinimumShareMessage = xNav.SelectSingleNode(@"//Settings/SharingRules/MinimumShareMessage").Value;

			_documentLoaded = true;
		}

		public static string ListSettings()
		{
			if (!_documentLoaded)
				return "";

			StringBuilder sb = new StringBuilder();
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "HubIdentity.Name", HubIdentity.Name));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "HubIdentity.Description", HubIdentity.Description));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "HubIdentity.Website", HubIdentity.Website));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "HubIdentity.HubOwner", HubIdentity.HubOwner));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "HubIdentity.MessageOfTheDay", HubIdentity.MessageOfTheDay));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "HubIdentity.UserInterfaceClientId", HubIdentity.UserInterfaceClientId));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "HubIdentity.FailoverAddresses", HubIdentity.FailoverAddresses));
			foreach (System.Net.EndPoint ep in HubConnection.ListeningEndPoints)
			{
				if (ep is System.Net.IPEndPoint)
				{
					System.Net.IPEndPoint iep = ep as System.Net.IPEndPoint;
					sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "HubConnection.Listeners", iep.ToString()));
				} 
				else if (ep is System.Net.DnsEndPoint)
				{
					System.Net.DnsEndPoint dep = ep as System.Net.DnsEndPoint;
					sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "HubConnection.Listeners", dep.Host + ":" + dep.Port.ToString()));
				}
			}
			foreach (HubConnection.Rules.DefaultRole defRole in HubConnection.Rules.DefaultRoles)
			{
				sb.AppendLine(String.Format("Variable Name: {0}, Value: RoleId({1}), AncestorId({2}), IPRangeStart({3}), IPRangeEnd({4})", "HubConnection.Rules.DefaultRole", defRole.RoleId, defRole.AncestorId, defRole.IPRangeStart, defRole.IPRangeEnd));
			}
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "HubConnection.IdentifyTimeout", HubConnection.IdentifyTimeout));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "HubConnection.MaximumUsersCount", HubConnection.MaximumUsersCount));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "HubConnection.ADCS.IsEnabled", HubConnection.ADCS.IsEnabled));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "SecurityBot.ClientId", SecurityBot.ClientId));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "SecurityBot.SessionId", SecurityBot.SessionId));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "SecurityBot.Description", SecurityBot.Description));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "OperatorBot.ClientId", OperatorBot.ClientId));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "OperatorBot.SessionId", OperatorBot.SessionId));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "OperatorBot.Description", OperatorBot.Description));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "NicknameRules.MinimumLength", NicknameRules.MinimumLength));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "NicknameRules.MaximumLength", NicknameRules.MaximumLength));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "Chat.MaximumMessageLength", Chat.MaximumMessageLength));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "Chat.MaximumMessageLines", Chat.MaximumMessageLines));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "SharingRules.MinimumShareSize", SharingRules.MinimumShareSize));
			sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "SharingRules.MinimumShareMessage", SharingRules.MinimumShareMessage));
			// WHY does this line not get printed by Apex? it works fine in a BMSG, but not an EMSG :S
			//sb.AppendLine(String.Format("Variable Name: {0}, Value: {1}", "NicknameRules.AllowedCharacters", NicknameRules.AllowedCharacters));
			return sb.ToString();
		}

		private static void CreateSettingsFile()
		{
			if (!CheckStoreExists())
				return;

			LogManager.WriteEntry("Creating Hub Settings", LogEventId.CreatingHubSettings, LogLevel.Detailed);

			XmlTextWriter xWriter = _settingsStore.CreateWriter();
			xWriter.Formatting = Formatting.Indented;

			xWriter.WriteStartDocument();
			xWriter.WriteStartElement("Settings");

			// Hub Identity
			xWriter.WriteStartElement("HubIdentity");
			xWriter.WriteElementString("Name", "Netfraction Hub");
			xWriter.WriteElementString("Description", "Hub Description");
			xWriter.WriteElementString("Website", string.Empty);
			xWriter.WriteElementString("HubOwner", string.Empty);
			xWriter.WriteElementString("MessageOfTheDay", "Hub Message Of The Day");

			// Generate a random ClientId for the Hub's GUI
			Random r = new Random();
			byte[] cidBytes = new byte[24];
			r.NextBytes(cidBytes);
			xWriter.WriteElementString("UserInterfaceClientId", Network.ConnectionManager.GenerateClientId(Network.Protocol.TIGR.Security.Cryptography.Tiger.Create(), cidBytes));

			xWriter.WriteElementString("FailoverAddresses", string.Empty);
			xWriter.WriteEndElement();

			// Hub Connection
			xWriter.WriteStartElement("HubConnection");
			xWriter.WriteStartElement("Listeners");
			xWriter.WriteElementString("EndPoint", "localhost:6356");
			xWriter.WriteEndElement(); 
			xWriter.WriteStartElement("Rules");
			xWriter.WriteStartElement("DefaultRole");
			xWriter.WriteAttributeString("roleId", "6");
			xWriter.WriteAttributeString("ancestorId", "2");
			xWriter.WriteComment("ancestorId is optional");
			xWriter.WriteElementString("IPRangeStart", "0.0.0.0");
			xWriter.WriteElementString("IPRangeEnd", "255.255.255.255");
			xWriter.WriteEndElement();
			xWriter.WriteStartElement("DefaultRole");
			xWriter.WriteAttributeString("roleId", "6");
			xWriter.WriteAttributeString("ancestorId", "2");
			xWriter.WriteElementString("IPRangeStart", "0:0:0:0:0:0:0:0");
			xWriter.WriteElementString("IPRangeEnd", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
			xWriter.WriteEndElement();
			xWriter.WriteEndElement();
			xWriter.WriteElementString("IdentifyTimeout", "90");
			xWriter.WriteElementString("MaximumUsersCount", "5000");
			xWriter.WriteStartElement("ADCS");
			xWriter.WriteElementString("IsEnabled", "False");
			xWriter.WriteEndElement();
			xWriter.WriteEndElement();

			// Hub Bots
			xWriter.WriteStartElement("HubBots");
			xWriter.WriteStartElement("SecurityBot");
			xWriter.WriteElementString("ClientId", "R2UWZEGET5LR3FGLEVPKGYL5ASMW33XZ6AC5YLY");
			xWriter.WriteElementString("SessionId", "DCBA");
			xWriter.WriteElementString("Description", "The town sheriff");
			xWriter.WriteEndElement();
			xWriter.WriteStartElement("OperatorBot");
			xWriter.WriteElementString("ClientId", "O6R5EGD4IU2HRWCBBHU3L2L2O77EMF3JAOJHJOI");
			xWriter.WriteElementString("SessionId", "ABCD");
			xWriter.WriteElementString("Description", "");
			xWriter.WriteEndElement();
			xWriter.WriteEndElement();

			// Nickname Rules
			xWriter.WriteStartElement("NicknameRules");
			xWriter.WriteElementString("MinimumLength", "2");
			xWriter.WriteElementString("MaximumLength", "32");
			xWriter.WriteComment("AllowedCharacters uses a C# regular expression.");
			xWriter.WriteComment("This is the maximum for the ADC spec: any Unicode character above 0x0020 (the space).");
			xWriter.WriteElementString("AllowedCharacters", @"[^\u0000-\u0020]+");
			xWriter.WriteEndElement();

			// Chat
			xWriter.WriteStartElement("Chat");
			xWriter.WriteElementString("MaximumMessageLength", "1024");
			xWriter.WriteElementString("MaximumMessageLines", "8");
			xWriter.WriteEndElement();

			// Sharing
			xWriter.WriteStartElement("SharingRules");
			xWriter.WriteElementString("MinimumShareSize", "0");
			xWriter.WriteElementString("MinimumShareMessage", "You must share at least 0B to share in this hub.");
			xWriter.WriteEndElement();

			xWriter.WriteEndElement();
			xWriter.WriteEndDocument();

			xWriter.Flush();
			xWriter.Close();
		}

		public static void SaveSettings()
		{
			if (!CheckStoreExists())
				return;

			LogManager.WriteEntry("Saving Hub Settings to: " + _settingsStore.FullPath, LogEventId.SavingHubSettings, LogLevel.Basic);

			try
			{
				XmlTextWriter xWriter = _settingsStore.CreateWriter();
				xWriter.Formatting = Formatting.Indented;

				xWriter.WriteStartDocument();
				xWriter.WriteStartElement("Settings");

				// Hub Identity
				xWriter.WriteStartElement("HubIdentity");
				xWriter.WriteElementString("Name", HubIdentity.Name);
				xWriter.WriteElementString("Description", HubIdentity.Description);
				xWriter.WriteElementString("Website", HubIdentity.Website);
				xWriter.WriteElementString("HubOwner", HubIdentity.HubOwner);
				xWriter.WriteElementString("MessageOfTheDay", HubIdentity.MessageOfTheDay);
				xWriter.WriteElementString("UserInterfaceClientId", HubIdentity.UserInterfaceClientId);
				xWriter.WriteElementString("FailoverAddresses", HubIdentity.FailoverAddresses);
				xWriter.WriteEndElement();

				// Hub Connection
				xWriter.WriteStartElement("HubConnection");
				xWriter.WriteStartElement("Listeners");
				foreach (System.Net.EndPoint ep in HubConnection.ListeningEndPoints)
				{
					if (ep is System.Net.IPEndPoint)
					{
						System.Net.IPEndPoint iep = ep as System.Net.IPEndPoint;
						xWriter.WriteElementString("EndPoint", string.Format("{0}:{1}", iep.Address, iep.Port));
					}
					else if (ep is System.Net.DnsEndPoint)
					{
						System.Net.DnsEndPoint dep = ep as System.Net.DnsEndPoint;
						xWriter.WriteElementString("EndPoint", string.Format("{0}:{1}", dep.Host, dep.Port));
					}
				}
				xWriter.WriteEndElement();

				xWriter.WriteStartElement("Rules");
				foreach (HubConnection.Rules.DefaultRole defRole in HubConnection.Rules.DefaultRoles)
				{
					xWriter.WriteStartElement("DefaultRole");
					xWriter.WriteAttributeString("roleId", defRole.RoleId.ToString());
					// always write this, even though it's optional. it'll just write byte.MAX_VALUE if not set
					xWriter.WriteAttributeString("ancestorId", defRole.AncestorId.ToString());
					xWriter.WriteElementString("IPRangeStart", defRole.IPRangeStart.ToString());
					xWriter.WriteElementString("IPRangeEnd", defRole.IPRangeEnd.ToString());
					xWriter.WriteEndElement();
				}
				xWriter.WriteEndElement();
				
				xWriter.WriteElementString("IdentifyTimeout", HubConnection.IdentifyTimeout.ToString());
				xWriter.WriteElementString("MaximumUsersCount", HubConnection.MaximumUsersCount.ToString());

				xWriter.WriteStartElement("ADCS");
				xWriter.WriteElementString("IsEnabled", HubConnection.ADCS.IsEnabled.ToString());
				xWriter.WriteEndElement();
				xWriter.WriteEndElement();

				// Hub Bots
				xWriter.WriteStartElement("HubBots");
				xWriter.WriteStartElement("SecurityBot");
				xWriter.WriteElementString("ClientId", "R2UWZEGET5LR3FGLEVPKGYL5ASMW33XZ6AC5YLY");
				xWriter.WriteElementString("SessionId", "DCBA");
				xWriter.WriteElementString("Description", SecurityBot.Description);
				xWriter.WriteEndElement();
				xWriter.WriteStartElement("OperatorBot");
				xWriter.WriteElementString("ClientId", "O6R5EGD4IU2HRWCBBHU3L2L2O77EMF3JAOJHJOI");
				xWriter.WriteElementString("SessionId", "ABCD");
				xWriter.WriteElementString("Description", OperatorBot.Description);
				xWriter.WriteEndElement();
				xWriter.WriteEndElement();

				// Nickname Rules
				xWriter.WriteStartElement("NicknameRules");
				xWriter.WriteElementString("MinimumLength", NicknameRules.MinimumLength.ToString());
				xWriter.WriteElementString("MaximumLength", NicknameRules.MaximumLength.ToString());
				xWriter.WriteComment("AllowedCharacters uses a C# regular expression.");
				xWriter.WriteComment("This is the maximum for the ADC spec: any Unicode character above 0x0020 (the space).");
				xWriter.WriteElementString("AllowedCharacters", NicknameRules.AllowedCharacters);
				xWriter.WriteEndElement();

				// Chat
				xWriter.WriteStartElement("Chat");
				xWriter.WriteElementString("MaximumMessageLength", Chat.MaximumMessageLength.ToString());
				xWriter.WriteElementString("MaximumMessageLines", Chat.MaximumMessageLines.ToString());
				xWriter.WriteEndElement();

				// Sharing
				xWriter.WriteStartElement("SharingRules");
				xWriter.WriteElementString("MinimumShareSize", SharingRules.MinimumShareSize.ToString());
				xWriter.WriteElementString("MinimumShareMessage", SharingRules.MinimumShareMessage.ToString());
				xWriter.WriteEndElement();


				xWriter.WriteEndElement();
				xWriter.WriteEndDocument();

				xWriter.Flush();
				xWriter.Close();
			}
			catch (Exception e)
			{
				LogManager.WriteEntry(e.Message + "\n" + e.TargetSite.Name, LogEventId.SettingsFileError, LogLevel.Basic);
			}
		}

		public static class HubIdentity
		{
			public static string Name;
			public static string Description;
			public static string Website;
			public static string HubOwner;
			public static string MessageOfTheDay;
			public static string UserInterfaceClientId;
			public static string FailoverAddresses;
		}

		public static class HubConnection
		{
			public static List<System.Net.EndPoint> ListeningEndPoints = new List<System.Net.EndPoint>();
			public static class Rules
			{
				public static List<DefaultRole> DefaultRoles = new List<DefaultRole>();
				public class DefaultRole
				{
					public byte RoleId;
					public byte AncestorId = byte.MaxValue;
					public System.Net.IPAddress IPRangeStart = System.Net.IPAddress.None;
					public System.Net.IPAddress IPRangeEnd = System.Net.IPAddress.None;
				}
			}
			public static int IdentifyTimeout;
			public static int MaximumUsersCount;
			public static class ADCS
			{
				public static bool IsEnabled;
			}
		}

		public static class SecurityBot
		{
			public static string ClientId;
			public static string SessionId;
			public static string Description;
		}

		public static class OperatorBot
		{
			public static string ClientId;
			public static string SessionId;
			public static string Description;
		}

		public static class NicknameRules
		{
			public static int MinimumLength;
			public static int MaximumLength;
			public static string AllowedCharacters;
		}

		public static class Chat
		{
			public static int MaximumMessageLength;
			public static int MaximumMessageLines;
		}

		public static class SharingRules
		{
			public static long MinimumShareSize;
			public static string MinimumShareMessage;
		}
	}
}
